#include <event2/event.h>
#include <pthread.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
//file
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
//net
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <iostream>
#include <vector>

// 
void agent_read_file(evutil_socket_t fd, short flag, void *arg);
void agent_wirte_net(evutil_socket_t fd, short flag, void *arg);
void judge_connect(evutil_socket_t fd, short flag, void *arg);
void* worker(void *param);

struct Handle {
    event_base *base;
    event *net_w_ev;
    event *file_r_ev;
    int net_fd;
    int fd;
    char buff[1024];
    int len;
    bool over;
};

char server_ip[32] = {"127.0.0.1"};
int server_port = 5060;

int main(int argc, char** argv) {
    if (argc > 2) {
        strcpy(server_ip, argv[1]);
        if (argc == 3) {
            server_port = atoi(argv[2]);
        }
    }
    std::cout << server_ip << ":" << server_port << std::endl;
    std::vector<pthread_t> pthids;

    for (int i = 0; i < 100; i++) {
        pthread_t pthd_t;
        if (pthread_create(&pthd_t, NULL, worker, (void*)NULL) != 0) {
            std::cout << "pthread_create error: " << i << std::endl;
        } else {
            //std::cout << "pthread_create succ : " << pthd_t << std::endl;
            pthids.push_back(pthd_t);
        }
    }

    //std::cout << "master join" << std::endl;
    for (std::vector<pthread_t>::iterator itr = pthids.begin();itr != pthids.end();itr++) {
        int *ret = new int;
        pthread_join(*itr, (void**) &ret);
        std::cout << *itr << " " << *ret << std::endl;
    }

    //sleep(1000);
}

void* worker(void *param) {
    int ret = 0;

    int fd = open("test.dat", O_RDONLY);
    if (fd < 0) {
        std::cout << "open file fail" << std::endl;
        ret = -1;
        pthread_exit((void*)&ret);
    }

    int net_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (net_fd == -1) {
        std::cout << "socket fail" << std::endl;
        ret = -2;
        pthread_exit((void*)&ret);
    }
    evutil_make_socket_nonblocking(net_fd);

    struct sockaddr_in server_addr;
    server_addr.sin_family      = AF_INET;
    server_addr.sin_port        = htons(server_port);
    inet_aton("127.0.0.1", &server_addr.sin_addr);

    if (0 == connect(net_fd, (const sockaddr*)&server_addr, sizeof(server_addr))) {
        //std::cout << "connect success" << std::endl;
    } else {
        //std::cout << "connect return " << errno << std::endl;
        if (errno != EINPROGRESS) {
            std::cout << "connect fail" << std::endl;
            close(net_fd);
            ret = -3;
            pthread_exit((void*)&ret);
        }
    }

    struct event_config *cfg = event_config_new();
    event_config_avoid_method(cfg, "epoll"); // epoll不支持本地文件，所以选用其他io多路复用方案
    struct event_base *base = event_base_new_with_config(cfg); //event_base_new();
    Handle *h = new Handle;
    h->len = 0;
    h->over = false;
    h->fd = fd;
    h->net_fd = net_fd;
    h->base = base;

    //std::cout << "[debug]" << net_fd << " " << fd << std::endl;
    h->net_w_ev = event_new(base, net_fd, EV_READ|EV_WRITE, judge_connect, (void*)h);
    h->file_r_ev = event_new(base, fd, (short)EV_READ, agent_read_file, (void*)h);

    if (h->net_w_ev == NULL || h->file_r_ev == NULL) {
        std::cout << "event_new fail" << std::endl;
        ret = -4;
        pthread_exit((void*)&ret);
    }

    if (0 == event_add(h->net_w_ev, NULL)) {
        int loop_ret = event_base_dispatch(base); // not return
        if (loop_ret != 1) {
            std::cout << "work over " << loop_ret << std::endl;
        }
        ret = 0;
        pthread_exit((void*)&ret);
    }
    
    //std::cout << "event_add fail" << std::endl;
}

void judge_connect(evutil_socket_t fd, short flag, void *arg) {
    int e;
    socklen_t elen = sizeof(e);

    if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void*)&e, &elen) < 0)
        return;

    if (e) {
        std::cout << "judge_connect fail" << std::endl;
        return;
    }

    Handle *h = (Handle *)arg;

    // 去读文件
    event_add(h->file_r_ev, NULL);
}

void agent_wirte_net(evutil_socket_t fd, short flag, void *arg) {
    Handle *h = (Handle *)arg;
    if (h->len > 0) {
        int c = send(fd, h->buff, h->len, 0);
        //std::cout << "send to net " << c << std::endl;
        if (c <= 0) {
            std::cout << "send error: " << errno << std::endl;
            close(h->fd);
            h->fd = -1;
            return;
        }
    }

    if (! h->over) {
        event_add(h->file_r_ev, NULL);
    }
}

void agent_read_file(evutil_socket_t fd, short flag, void *arg) {
    Handle *h = (Handle *)arg;
    h->len = read(fd, h->buff, 1024);
    if (h->len <= 0) {
        close(h->fd);
        h->fd = -1;
        h->over = true;
    }

    //std::cout << "from file read " << h->len << std::endl;
    event_assign(h->net_w_ev, h->base, h->net_fd, EV_WRITE, agent_wirte_net, arg);
    event_add(h->net_w_ev, NULL);
}
